import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:stack_trace/stack_trace.dart';

// 移除平台通道依赖，集中在 Dart 侧输出
import 'dart_file_logger.dart';
import 'log_level.dart';
import 'log_config.dart';
import 'log_style_config.dart';

/// 日志工具方法集合，承载核心格式化与输出逻辑。
class LogUtils {
  /// 判断当前日志级别是否应该输出。
  static bool shouldLog(LogLevel level, PDLogConfig config) {
    if (!config.enabled) return false;
    return level.index >= config.minLevel.index;
  }

  /// 从当前调用栈中提取调用者函数名称。
  ///
  /// 解析规则：跳过来自 PDLog 的栈帧与 dart:async 等框架栈，
  /// 返回第一个业务代码栈帧的函数标识（可能是 `Class.method` 或函数名）。
  /// 不同平台的栈格式略有差异，本方法尽量做到兼容，失败时返回 null。
  static String? getCallerFunctionName(StackTrace stackTrace) {
    try {
      // 使用 stack_trace 统一解析各平台栈格式
      final trace = Trace.from(stackTrace);
      for (final f in trace.frames) {
        final file = f.uri.toString();
        final member = f.member ?? 'unknown';
        final lineNo = f.line;
        // 跳过框架与本包源码
        if (_isFrameworkFile(file) || _isPdLogPackageRef(file)) continue;
        if (member.contains('LogUtils') || member.contains('PDLog')) continue;
        if (lineNo != null) {
          return '$member @ $file:$lineNo';
        } else {
          return '$member @ $file';
        }
      }
    } catch (_) {
      // 解析失败时返回空
    }
    return null;
  }

  /// 判断是否为 Flutter/Dart 框架文件（需要跳过）。
  static bool _isFrameworkFile(String file) {
    return file.startsWith('dart:') ||
        file.startsWith('package:flutter/') ||
        file.contains('/flutter/src/');
  }

  /// 严格判断是否为 pd_log 包源码引用：
  /// 仅匹配 `package:pd_log/...` 或源码路径中包含 `/pd_log/lib/`，
  /// 避免误匹配 `package:pd_log_example` 这类示例包名。
  static bool _isPdLogPackageRef(String text) {
    return RegExp(r'package:pd_log(/|$)').hasMatch(text) ||
        text.contains('/pd_log/lib/');
  }

  /// 将任意对象安全转换为字符串：
  /// - `null` -> "(null)"
  /// - `String` 原样返回
  /// - `Map`/`Iterable` -> `jsonEncode`
  /// - 其他类型 -> `toString()`
  static String stringify(Object? message) {
    if (message == null) return '(null)';
    if (message is String) return message;
    try {
      if (message is Map || message is Iterable) {
        return jsonEncode(message);
      }
    } catch (_) {
      // fallthrough to toString
    }
    return message.toString();
  }

  /// 核心日志输出方法：
  /// - 根据配置判断是否输出；
  /// - 格式化带上级别、标签与时间戳；
  /// - 控制台打印与文件写入全部在 Dart 侧实现；
  /// - 可选择指定本条日志是否写入本地文件（Dart 写入器）。
  static void log(
    LogLevel level,
    Object? message, {
    String? tag,
    bool? useConsole,
    bool? toFile,
    bool? showTimestamp,
    bool? showCaller,
    LogStyleConfig? style,
    required PDLogConfig config,
  }) {
    if (!shouldLog(level, config)) return;

    final now = DateTime.now();
    final ts =
        (showTimestamp ?? config.showTimestamp) ? now.toIso8601String() : null;
    final resolvedTag = tag ?? config.defaultTag;
    final wantCaller = showCaller ?? config.showCaller;
    String? callerInfo;
    if (wantCaller) {
      final caller = getCallerFunctionName(StackTrace.current);
      if (caller != null && caller.isNotEmpty) {
        callerInfo = '[$caller]';
      }
    }

    final msgText = stringify(message);
    // 可读性与性能优化：拆分可选片段并一次性拼接
    final tagPart = resolvedTag != null ? '[$resolvedTag]' : '';
    final tsPart = ts != null ? '[$ts]' : '';
    final callerPart = callerInfo ?? '';
    final splicingMessages =
        [tsPart, callerPart, msgText].where((s) => s.isNotEmpty).join(' ');

    // 文件写入是否允许（同时满足开启与级别阈值）
    final bool fileWriteAllowed = toFile ??
        (config.nativeFileLoggingEnabled) &&
            (level.index >= config.fileLoggingMinLevel.index);

    // 控制台打印开关（仅由 useConsole/config.useConsole 控制）
    final bool printToConsole = (useConsole ?? config.useConsole);

    var start = style?.ansiStartSequence ??
        (config.logStyles[level]?.ansiStartSequence ?? '');
    var end = LogStyleConfig.ansiReset;
    if (!config.ansiEnabled) {
      start = '';
      end = '';
    }

    // 控制台输出（Dart 侧）
    if (printToConsole && kDebugMode) {
      final formatted = '[${level.short}]$tagPart$splicingMessages';
      final colored = _applyAnsiPerLine(formatted, start, end);
      debugPrint(colored);
    }

    // 文件写入（Dart 写入器）
    if (fileWriteAllowed) {
      final plainFileLine = '[$resolvedTag]$splicingMessages\n';
      DartFileLogger.instance.enqueue(plainFileLine);
    }
  }

  /// 针对包含换行的文本，将 ANSI 样式逐行应用，避免样式在换行后丢失。
  static String _applyAnsiPerLine(String text, String start, String reset) {
    if (start.isEmpty) return text;
    final lines = text.split('\n');
    final styled = lines.map((l) => '$start$l$reset').join('\n');
    return text.endsWith('\n') ? '$styled\n' : styled;
  }

  /// JSON 美化打印：将对象格式化为易读的 JSON 字符串并输出。
  ///
  /// - 使用 [formatJson] 将对象规范化并序列化；
  /// - 可按需写入本地文件（受级别与配置约束，由 Dart 写入器处理）；
  /// - 支持设置级别、标签与样式；
  /// - 控制台输出由上层开关决定（此处默认不在 Dart 控制台打印）。
  static void formated(
    Object? message, {
    LogLevel level = LogLevel.verbose,
    String? tag,
    bool? toFile,
    LogStyleConfig? style,
    required PDLogConfig config,
  }) {
    final formattedMessage = formatJson(message);
    log(
      level,
      formattedMessage,
      useConsole: false,
      toFile: toFile,
      showTimestamp: config.showTimestamp,
      style: style,
      config: config,
    );
  }

  static String formatJson(Object? message) {
    try {
      // 使用自定义 toEncodable，确保：
      // 1) Map 的 key 全部转为字符串（避免非字符串 key 导致 JSON 编码失败）
      // 2) 递归处理 Iterable/Map，保留基础类型，DateTime 转 ISO8601，其他对象尽量使用 toJson，否则 toString
      final encoder = JsonEncoder.withIndent('  ', (dynamic obj) {
        return _normalize(obj);
      });
      return encoder.convert(_normalize(message));
    } catch (_) {
      // 如果JSON转换失败，回退到普通字符串化
      return stringify(message);
    }
  }

  static dynamic _normalize(Object? obj) {
    if (obj == null || obj is num || obj is bool || obj is String) return obj;
    if (obj is DateTime) return obj.toIso8601String();
    if (obj is Map) {
      // 将所有 key 转为字符串，并递归规范化 value
      return obj.map((k, v) => MapEntry(k.toString(), _normalize(v)));
    }
    if (obj is Iterable) {
      return obj.map((e) => _normalize(e)).toList();
    }
    // 尝试使用 toJson
    try {
      final dynamic json = (obj as dynamic).toJson();
      return _normalize(json);
    } catch (_) {
      // 最后兜底为字符串
      return obj.toString();
    }
  }
}
